/*	eh++.h	*/

#pragma once

LPCTSTR GetFunctionName(UINT function_id);
LPCTSTR GetModuleName(UINT module_id);

#ifdef _DEBUG
#define DEBUG_QUOTE(x) #x
#else
#define DEBUG_QUOTE(x) ""
#endif

#ifdef FULL_RELEASE
#define REQUIRE(boolean_expression, requirement_number) 
#define PROMISE(boolean_expression, promise_number) 
#define SANITY_CHECK(boolean_expression, check_number) 
#else
#define REQUIRE(boolean_expression, requirement_number) \
	if (!(boolean_expression)) \
	{	\
		throw RequirementNotMetException(THIS_MODULE, THIS_FUNCTION, requirement_number, DEBUG_QUOTE(boolean_expression));	\
	}

#define PROMISE(boolean_expression, promise_number) \
	if (!(boolean_expression)) \
	{	\
		throw PromiseNotKeptException(THIS_MODULE, THIS_FUNCTION, promise_number, DEBUG_QUOTE(boolean_expression)); \
	}

#define SANITY_CHECK(boolean_expression, check_number) \
	if (!(boolean_expression))	\
	{	\
		throw SanityCheckFailedException(THIS_MODULE, THIS_FUNCTION, check_number, DEBUG_QUOTE(boolean_expression)); \
	}
#endif

class Exception : public exception
{
protected:
	UINT		error_code;
	TCHAR		description[2048];
public:
	Exception(UINT code, LPCTSTR desc)	{	error_code = code; wsprintf(description, desc); }
	Exception()											{	error_code = 0; wsprintf(description, "No Details!");	}
	Exception(LPCTSTR desc)					{	error_code = 0; wsprintf(description, desc); }
	Exception(const Exception &e)					{	error_code = e.GetErrorCode(); wsprintf(description, e.what());	}

	Exception(UINT module_id, UINT function_id, UINT phase_id, const Exception &e)
		{
			error_code = e.GetErrorCode();
#ifdef _DEBUG
			wsprintf(description, "%s\n\nIn Module: %s\nFunction: %s\nPhase:%d", e.what(), GetModuleName(module_id), GetFunctionName(function_id), phase_id);	
#elif FULL_RELEASE
			wsprintf(description, "%s", e.what());	
#else
			wsprintf(description, "%s\n\nIn Module: %d\nFunction: %d\nPhase:%d", e.what(), module_id, function_id, phase_id);	
#endif
		}

	const char* what() const { return description;	} 
	TCHAR		GetErrorCode() const	{ return error_code;	}
}; 

class WindowsError : public Exception
{
public:
	WindowsError(TCHAR code)	{ error_code = code; ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), description, 2048, NULL); }
	WindowsError()				{ WindowsError((TCHAR)::GetLastError()); }
};

class WindowsException : public Exception //, EXCEPTION_RECORD
{
public:
	WindowsException(TCHAR u, EXCEPTION_POINTERS* exp_ptr)
	{
		error_code = u;
		//you ought to piece together the interesting info here into a description
	}
};

class FailedAssertionException : public Exception
{};

class RequirementNotMetException : public FailedAssertionException
{
public:
	RequirementNotMetException(UINT module_id, UINT function_id, TCHAR phase_id, LPTSTR condition);
};

class PromiseNotKeptException : public FailedAssertionException 
{
public:
	PromiseNotKeptException(UINT module_id, UINT function_id, TCHAR phase_id, LPTSTR condition);
};

class SanityCheckFailedException : public FailedAssertionException 
{
public:
	SanityCheckFailedException(UINT module_id, UINT function_id, TCHAR phase_id, LPTSTR condition);
};


inline	RequirementNotMetException::RequirementNotMetException(UINT module_id, UINT function_id, TCHAR phase_id, LPTSTR condition)
{
#ifdef _DEBUG
	wsprintf(description, "Requirement Not Met:\n%s\n\nIn Module: %s\nFunction: %s\nRequirement Number:%d", condition, GetModuleName(module_id), GetFunctionName(function_id), phase_id);	
#elif FULL_RELEASE
	wsprintf(description, "Requirement Not Met");	
#else
	wsprintf(description, "Requirement Not Met:\n\nIn Module: %d\nFunction: %d\nRequirement Number:%d", module_id, function_id, phase_id);	
#endif
}

inline	PromiseNotKeptException::PromiseNotKeptException(UINT module_id, UINT function_id, TCHAR phase_id, LPTSTR condition)
{
#ifdef _DEBUG
	wsprintf(description, "Promise Not Kept:\n%s\n\nIn Module: %s\nFunction: %s\nPromise Number:%d", condition, GetModuleName(module_id), GetFunctionName(function_id), phase_id);	
#elif FULL_RELEASE
	wsprintf(description, "Promise Not Kept");	
#else
	wsprintf(description, "Promise Not Kept:\n\nIn Module: %d\nFunction: %d\nPromise Number:%d", module_id, function_id, phase_id);	
#endif
}

inline	SanityCheckFailedException::SanityCheckFailedException(UINT module_id, UINT function_id, TCHAR phase_id, LPTSTR condition)
{
#ifdef _DEBUG
	wsprintf(description, "Sanity Check Failed:\n%s\n\nIn Module: %s\nFunction: %s\nSanity Check Number:%d", condition, GetModuleName(module_id), GetFunctionName(function_id), phase_id);	
#elif FULL_RELEASE
	wsprintf(description, "Sanity Check Failed");	
#else
	wsprintf(description, "Sanity Check Failed\n\nIn Module: %d\nFunction: %d\nSanity Check Number:%d", module_id, function_id, phase_id);	
#endif
}

